/*
 ============================================================================
 Name        : hcputils.c
 Version     : 4.1
 Description : Coding sample for general operations used by the HCP samples.

 License     :
               Copyright (c) 2011 Hitachi Data Systems, Inc.

               Permission is hereby granted to  this software and associated
               documentation files (the "Software"), subject to the terms and
               conditions of the Sample Source Code License (SSCL) delivered
               with this Software. If you do not agree to the terms and
               conditions of the SSCL,

                 (i)  you must close this file and delete all copies of the
                      Software, and
                 (ii) any permission to use the Software is expressly denied.

 ============================================================================
 */

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include <curl/curl.h>

#include <openssl/evp.h>

#include "hcpUtils.h"

/* ---- Base64 Encoding/Decoding Table --- */
static const char table64[]=
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

#define HCP_AUTH_HEADER_FORMAT "Authorization: HCP %s:%s"

/**
 * Function to process the HTTP headers returned from requests. This implementation
 * dumps out the header to standard out (great for debugging/development).
 *
 * If provided to the libcurl CURLOPT_HEADERFUNCTION, it will be executed.
 */
size_t headerProcessor(void *ptr, size_t size, size_t nmemb, void *userdata) {
	char *buff = malloc((size*nmemb)+1);
	char *inPtr = (char*)ptr;

	int outIdx = 0;
	int inIdx = 0;

	// Strip out any control characters.
	for (; inIdx < size*nmemb; inIdx++) {
		// Strip out any special chars.  I'm a Linux/Unix bigot.
		if (!iscntrl(inPtr[inIdx]))
			buff[outIdx++] = inPtr[inIdx];
	}

	buff[outIdx] = '\0';  // Null terminate.

	// Dump out all headers for debugging purposes.
	printf("%s\n", buff);

	free(buff);

	return nmemb*size;
}

/*
 * Routine to create an MD5 hash and return the string pointed to
 *   by the function return value. The caller is responsible to free
 *   the memory after it is done.
 */
char *MD5HashString(const char *inStr)
{
	EVP_MD_CTX hash;
	unsigned int hashi;
	unsigned char hash_value[EVP_MAX_MD_SIZE] = {0};

	char *outStr = NULL;

	EVP_DigestInit(&hash, EVP_md5());
	EVP_DigestUpdate(&hash, inStr, strlen(inStr));
	EVP_DigestFinal(&hash, hash_value, &hashi);

	outStr = malloc( ( hashi * 2 ) + 1);

	if (NULL != outStr) {
		int i;
		for (i=0; i < hashi; i++)
			sprintf(&outStr[2*i], "%02hhX", hash_value[i]);
	}

	return outStr;
}

/*
 * Routine to create a base64 encoded string from the input string.
 *   This function will allocate the space for the encoding, and it is
 *   the callers responsibility to free the memory after it is done.
 *
 * NOTE: C/C++ does not have a stock library that has base64 encoding.
 *   However, a static library for base64 can be built from sources found
 *   at http://libb64.sourceforge.net/.  But here is an implementation to
 *   help keep the sample have less dependencies.
 */
char *Base64EncodeString(const char *inStr) {
  char *retVal, *outPtr;

  const char *inPtr = inStr;
  int insize = strlen(inPtr);

  retVal = outPtr = malloc(insize*4/3+4);

  if (NULL != retVal) {
	  while(insize > 0) {

        unsigned char ibuf[3];
        unsigned char obuf[4];

        int i;
        int inputparts;

        // Fill up inbuf with 3 chars and zero for any remaining.
        for (i = inputparts = 0; i < 3; i++) {
	      if(insize > 0) {
	        inputparts++;
	        ibuf[i] = (unsigned char) *inPtr;
	        inPtr++;
	        insize--;
	      }
	      else
	        ibuf[i] = 0;
	    }

        // Translate the ibuf into obuf doing appropriate shifting
        obuf[0] = (unsigned char)  ((ibuf[0] & 0xFC) >> 2);
	    obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
	                               ((ibuf[1] & 0xF0) >> 4));
	    obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
	                               ((ibuf[2] & 0xC0) >> 6));
	    obuf[3] = (unsigned char)   (ibuf[2] & 0x3F);

	    // Depending on how many characters we processed, create a string equiv.
	    switch(inputparts) {
	    case 1: /* only one byte read */
	      snprintf(outPtr, 5, "%c%c==",
	               table64[obuf[0]],
	               table64[obuf[1]]);
	      break;
	    case 2: /* two bytes read */
	      snprintf(outPtr, 5, "%c%c%c=",
	               table64[obuf[0]],
	               table64[obuf[1]],
	               table64[obuf[2]]);
	      break;
	    default:
	      snprintf(outPtr, 5, "%c%c%c%c",
	               table64[obuf[0]],
	               table64[obuf[1]],
	               table64[obuf[2]],
	               table64[obuf[3]] );
	      break;
	    }
	    outPtr += 4;
	  }

	  *outPtr = 0; // NULL terminate string.
  }

  return retVal;
}

/*
 * Routine to create the HCP authorization header in a string that consists of
 * a base64 encoded username and md5 encoded password.
 *
 * The caller is responsible for freeing the memory for the string created.
 */
char *createAuthHeader(char *inUserName, char *inPassword) {
	char *authHeader = NULL;

	// Get base64 encoded UserName
	char *b64UserName = Base64EncodeString(inUserName);

	// Get md5 encoded Password
	char *md5Password = MD5HashString(inPassword);

	if (NULL != b64UserName && NULL != md5Password)
	{
		// Put it all together in a string to pass back.
		authHeader = malloc(
			( strlen(HCP_AUTH_HEADER_FORMAT) - 4 )  // Cookie Format less 4 format characters
			+ strlen(b64UserName)
			+ strlen(md5Password)
			+ 1);

		if (NULL != authHeader)
			sprintf(authHeader, HCP_AUTH_HEADER_FORMAT, b64UserName, md5Password);
	}

	// Done with these.
	if (NULL != md5Password) free(md5Password);
	if (NULL != b64UserName) free(b64UserName);

	return authHeader;
}

